/***************************************************************************\
 * The Mattise Kernel														*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * devman.cc																*
 *																			*
 * Device manager, handles low-level I/O and driver installation			*
 *																			*
\***************************************************************************/

//------------- Includes -------------//
#include <system.h>
#include <LinkedList.hpp>
#include <string.h>
#include <stdio.h>
#include <io.h>

//------------- Macros -------------//
#define INVALID_DEV			( -1 )
#define VALID_DEV( dev )	( dev != -1 && dev < DeviceList.Size() )
#define VALID_DESC( fd )	( fd != -1 && fd < Descriptors.Size() )

//------------- Types -------------//

// device structure
struct Device {

	//------- Device Details -------//
	char name[64];	/** used in 'open' calls						**/
	int VendorID;	/** makes printing information easier			**/
	int DeviceID;	/** see above									**/
	int osid;		/** the operating system's ID for the device	**/
	int mode;		/** the device's 'open' mode					**/
	
	//------- Functions -------//
	int (*ioctl_int)(int,int,int);
	int (*ioctl_ptr)(int,int,void*);
	int (*read)(int,void*,uint_t);
	int (*write)(int,void*,uint_t);
	int (*lseek)(int,uint_t,int);
	int (*getc)();
	int (*putc)(int);
} __attribute((packed));

// a descriptor information structure
struct Descriptor {
	//------- Descriptor Information -------//
	char devname[64];	/** device name 							**/
	int mode;			/** device mode								**/
	int dev;			/** device id								**/
	
	//------- Functions -------//
	int (*ioctl_int)(int,int,int);
	int (*ioctl_ptr)(int,int,void*);
	int (*read)(int,void*,uint_t);
	int (*write)(int,void*,uint_t);
	int (*lseek)(int,uint_t,int);
	int (*getc)();
	int (*putc)(int);
} __attribute((packed));

// global linked list of all devices
CLinkedList<struct Device> DeviceList;
CLinkedList<struct Descriptor> Descriptors;

//------------- Global Routines -------------//

// returns a new file descriptor for the device
int open( const char* pathname, int flags )
{
	// find in the list
	for( int i = 0; i < DeviceList.Size(); i++ )
	{
		// get this device
		struct Device thisdev = DeviceList.Get(i);
		
		// compare
		if( strcmp( pathname, thisdev.name, strlen( thisdev.name ) ) )
		{
			// set up the descriptor the way we want it
			struct Descriptor d;
			
			// and then copy the name
			strcpy( d.devname, pathname );
			
			// set the flags
			d.mode = flags;
			
			// set the device id
			d.dev = i;
			
			// setup the functions
			d.ioctl_int = thisdev.ioctl_int;
			d.ioctl_ptr = thisdev.ioctl_ptr;

			// null all the read/write functions
			d.read = (ReadFunc) ( d.write = (WriteFunc) NULL );
			d.getc = (GetCFunc) ( d.putc = (PutCFunc) NULL );
			
			// READONLY = only read is available
			if( flags & READONLY )
			{
				d.getc = thisdev.getc;
				d.read = thisdev.read;
			}
			// READWRITE = read/write
			if( flags & READWRITE )
			{
				d.getc = thisdev.getc;
				d.read = thisdev.read;
				d.write = thisdev.write;
			}
			// WRITEONLY = write only, no read!
			if( flags & WRITEONLY )
			{
				d.putc = thisdev.putc;
				d.write = thisdev.write;
			}
				
			// seek is independent
			d.lseek = thisdev.lseek;
			
			// allocate a new descriptor for it
			int dsp = Descriptors.Push( d );
			
			// return the descriptor
			return dsp;
		}
	}
	
	// none found
	return INVALID_DEV;
}

// allows control of the device outside of the normal read/write
int ioctl( int fd, int request, int data )
{
	// check for valid file descriptor
	if( VALID_DESC( fd ) )
	{
		// good to go!
		if( Descriptors.Get(fd).ioctl_int )
			return Descriptors.Get(fd).ioctl_int( fd, request, data );
		else
			return -1;
	}
	else
	{
		return -1;
	}
}
int ioctl( int fd, int request, void* ptr )
{
	// check for valid file descriptor
	if( VALID_DESC( fd ) )
	{
		// good to go!
		if( Descriptors.Get(fd).ioctl_ptr )
			return Descriptors.Get(fd).ioctl_ptr( fd, request, ptr );
		else
			return -1;
	}
	else
	{
		return -1;
	}
}

// reads bytes into buff
int read( int fd, void* buff, uint_t count )
{
	// check for valid file descriptor
	if( VALID_DESC( fd ) )
	{
		// good to go!
		if( Descriptors.Get(fd).read )
			return Descriptors.Get(fd).read( fd, buff, count );
		else
			return -1;
	}
	else
	{
		return -1;
	}
}

// writes bytes from buff
int write( int fd, void* buff, uint_t count )
{
	// check for valid file descriptor
	if( VALID_DESC( fd ) )
	{
		// good to go!
		if( Descriptors.Get(fd).write )
			return Descriptors.Get(fd).write( fd, buff, count );
		else
			return -1;
	}
	else
	{
		return -1;
	}
}

// seeks to a location in the device
int lseek( int fd, uint_t offset, int where )
{
	// check for valid file descriptor
	if( VALID_DESC( fd ) )
	{
		// good to go!
		if( Descriptors.Get(fd).lseek )
			return Descriptors.Get(fd).lseek( fd, offset, where );
		else
			return -1;
	}
	else
	{
		return -1;
	}
}

// closes the descriptor
int close( int fd )
{	
	// delete it
	Descriptors.Delete( fd );
	
	// good!
	return 0;
}

// gets a character
int getc( int fd )
{
	// check for valid file descriptor
	if( VALID_DESC( fd ) )
	{
		// good to go!
		if( Descriptors.Get(fd).getc )
			return Descriptors.Get(fd).getc();
		else
			return -1;
	}
	else
	{
		return -1;
	}
}

// writes a character
int putc( int fd, int c )
{
	// check for valid file descriptor
	if( VALID_DESC( fd ) )
	{
		// good to go!
		if( Descriptors.Get(fd).putc )
			return Descriptors.Get(fd).putc( c );
		else
			return -1;
	}
	else
	{
		return -1;
	}
}

//------------- Device Management -------------//

// gets the device name
void GetDeviceName( int fd, char* dest )
{
	// copy it
	strcpy( dest, Descriptors.Get(fd).devname );
}

// gets the device id
int GetDeviceID( int fd )
{
	// return it
	return Descriptors.Get(fd).dev;
}

// installs a new device, and returns an index for use later
int InstallDevice( const char* devname, int VendorID, int DeviceID )
{
	// add it to the list
	struct Device newdev;
	memcpy( newdev.name, devname, strlen( devname ) );
	newdev.VendorID = VendorID;
	newdev.DeviceID = DeviceID;
	newdev.osid = DeviceList.Size();
	
	// push it onto the list
	DeviceList.Push( newdev );
	
	// success
	return newdev.osid;
}

// installs the functions necessary (takes pointers, these can be NULL)
int InstallFunctions( int dev, IOCtlIntFunc devioctl_int, IOCtlPtrFunc devioctl_ptr, ReadFunc devread, WriteFunc devwrite, LSeekFunc devlseek, GetCFunc devgetc, PutCFunc devputc )
{
	// get the device
	struct Device d = DeviceList.Get(dev);
	
	// add in the necessary data
	d.ioctl_int = devioctl_int;
	d.ioctl_ptr = devioctl_ptr;
	d.read = devread;
	d.write = devwrite;
	d.lseek = devlseek;
	d.getc = devgetc;
	d.putc = devputc;
	
	// insert the new data
	DeviceList.Insert( d, dev );
	DeviceList.Delete( dev );
}

// destroys a device
int DestroyDevice( int dev )
{
	// delete it
	DeviceList.Delete( dev );
	
	// return success
	return 0;
}

// dumps all the devices installed
void DumpDevices()
{
	// loop through
	for( int i = 0; i < DeviceList.Size(); i++ )
	{
		struct Device d = DeviceList.Get(i);
		kprintf( "Device %d: %s\n\tVendor ID: %d\n\tDevice ID: %d\n", i, d.name, d.VendorID, d.DeviceID );
	}
}
